Ingeniería Biomédica
2026-01-14
Definition
Noise refers to any unwanted or random variations in a signal that interfere with the desired information. It is an unpredictable disturbance that can distort or obscure the actual data, making it harder to interpret or analyze.
import numpy as np
import matplotlib.pyplot as plt
# Parámetros de la señal
duration = 2 # Duración en segundos
fs = 1000 # Frecuencia de muestreo en Hz
t = np.linspace(0, duration, duration * fs, endpoint=False) # Vector de tiempo
# Señal senoidal de 10 Hz
freq = 10
sine_wave = np.sin(2 * np.pi * freq * t)
# Señal de ruido aleatorio con distribución normal
noise_normal = np.random.normal(0, 1, len(t))
# Señal con ruido aleatorio de 2 a 5 Hz
low_freq_noise = np.sin(2 * np.pi * np.random.uniform(2, 5) * t)
signal_with_low_freq_noise = sine_wave + low_freq_noise
# Señal con ruido aleatorio uniforme sumado
uniform_noise = np.random.uniform(-0.5, 0.5, len(t))
signal_with_uniform_noise = sine_wave + uniform_noise
# Señal con ruido aleatorio uniforme multiplicado
multiplicative_noise = np.random.uniform(0.5, 1.5, len(t))
signal_with_mult_noise = sine_wave * multiplicative_noise
# Graficamos las señales
fig, axes = plt.subplots(5, 1, figsize=(10, 10), sharex=True)
axes[0].plot(t, sine_wave, label="Sine wave (10 Hz)")
axes[0].set_title("Sine Wave (10 Hz)")
axes[0].legend()
axes[1].plot(
t, noise_normal, label="Random Noise (Normal Distribution)", color="orange"
)
axes[1].set_title("Random Noise (Normal Distribution)")
axes[1].legend()
axes[2].plot(
t, signal_with_low_freq_noise, label="Sine + Low Freq Noise (2-5 Hz)", color="green"
)
axes[2].set_title("Sine + Low Freq Noise (2-5 Hz)")
axes[2].legend()
axes[3].plot(t, signal_with_uniform_noise, label="Sine + Uniform Noise", color="red")
axes[3].set_title("Sine + Uniform Noise")
axes[3].legend()
axes[4].plot(t, signal_with_mult_noise, label="Sine * Uniform Noise", color="purple")
axes[4].set_title("Sine * Uniform Noise")
axes[4].legend()
plt.xlabel("Time [s]")
plt.tight_layout()
plt.show()Definition
Analog signal processing (ASP) refers to the manipulation of continuous-time signals after they have been acquired from a transducer but before digital conversion. This type of processing is performed using electronic circuits that modify the signal in the analog domain to enhance its quality, extract useful information, or prepare it for further processing.
Common tasks
Definition
An analog-to-digital converter (ADC) is a device that converts a continuous-time signal, obtained through a transducer, into a digital signal that can be processed by a computer. This process consists of two fundamental operations, which occur simultaneously in practical implementations: sampling and quantization.
Input range: \(\left[V\_{\min},,V\_{\max}\right]\), bit depth \(b\), levels \(L=2^b\), step size (LSB)
\[ \Delta=\frac{V_{\max}-V_{\min}}{L}. \]
Mid-tread (round-to-nearest): \(Q(x)=\Delta,\mathrm{round}!\big(x/\Delta\big)\).
Mid-rise (truncate + half-step): \(Q(x)=\Delta\big(\lfloor x/\Delta\rfloor+\tfrac12\big)\).
Overload/clipping outside \(\left[V\_{\min},V\_{\max}\right]\): \(\tilde{x}=V\_{\max}\) or \(V\_{\min}\).
Decision thresholds at \(k\Delta\); reconstruction levels at:
Practical note: choose mid-tread for rounding semantics; mid-rise for deterministic staircase without zero level.
Error \(e=x-Q(x)\). Under high-resolution assumptions (no clipping, sufficiently dense input):
For a full-scale sinusoid:
\[ \mathrm{SNR}_{\mathrm{dB}}\approx 6.02\,b + 1.76. \]
With RMS usage fraction \(\rho\) of full scale (FS):
\[ \mathrm{SNR}_{\mathrm{dB}}\approx 6.02\,b + 1.76 + 20\log_{10}(\rho). \]
From a measured in-band SNR (RMS, same bandwidth):
\[ \mathrm{ENOB}\approx\frac{\mathrm{SNR}_{\mathrm{dB}}-1.76}{6.02}. \]
Use ENOB to compare real converters (including clock jitter, distortion) against ideal \(b\).
Analog gain \(G\) maps input to ADC: \(x\_{\text{ADC}}=G,x\_{\text{in}}\).
Input-referred LSB: \(\Delta\_{\text{in}}=\Delta/G\).
Design goals:
Suppose electrode-level ECG peaks \(\approx \pm 5,\mathrm{mV}\). Choose \(G=200\) so \(\pm 5,\mathrm{mV}\mapsto \pm 1,\mathrm{V}\) at ADC (\(V\_{\min,\max}=\pm 1,\mathrm{V}\)).
With \(b=12\):
If \(\rho\approx 0.5\), then \(\mathrm{SNR}\approx 6.02\cdot 12 + 1.76 - 6 \approx 68,\mathrm{dB}\) → typically adequate for diagnostic ECG.
For highly non-uniform amplitude distributions, companding allocates effective resolution to small amplitudes.
\(\mu\)-law (telephony):
\[ y=\mathrm{sgn}(x)\,\frac{\ln\big(1+\mu |x|/X_{\max}\big)}{\ln(1+\mu)},\quad \mu\approx 255. \]
In biomedicine, primary acquisition usually remains linear; companding is more relevant to low-bit-rate telemetry or storage.
# Synthetic ECG, quantization at 8/10/12 bits, SNR and plots
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
fs = 360.0
T = 5.0
t = np.arange(0, T, 1/fs)
def g(t, mu, sigma, A):
return A*np.exp(-0.5*((t-mu)/sigma)**2)
def ecg_template(t):
P = g(t, 0.20, 0.045, 0.10)
Q = g(t, 0.36, 0.010, -0.25)
R = g(t, 0.40, 0.012, 1.00)
S = g(t, 0.44, 0.016, -0.35)
Tn= g(t, 0.70, 0.080, 0.30)
return P + Q + R + S + Tn
hr = 60.0
RR = 60.0/hr
ecg_mV = np.zeros_like(t)
for k in range(int(np.ceil(T/RR))):
ecg_mV += ecg_template(t - k*RR)
wander = 0.05*np.sin(2*np.pi*0.3*t)
noise = 0.02*np.random.randn(len(t))
ecg_mV = ecg_mV + wander + noise
G = 200.0
Vfs = 1.0
Vmin, Vmax = -Vfs, Vfs
x_adc = (ecg_mV/1000.0)*G # mV -> V and gain
def quantize_uniform(x, bits, Vmin, Vmax, mid_tread=True):
x_clip = np.clip(x, Vmin, Vmax)
L = 2**bits
Delta = (Vmax - Vmin)/L
if mid_tread:
y = Delta*np.round(x_clip/Delta)
else:
y = Delta*(np.floor(x_clip/Delta) + 0.5)
y = np.clip(y, Vmin, Vmax)
return y, Delta
def snr_db(x, y):
e = x - y
x_ac = x - np.mean(x)
e_ac = e - np.mean(e)
Px = np.mean(x_ac**2)
Pe = np.mean(e_ac**2)
return 10*np.log10(Px/Pe), e
bits_list = [8, 10, 12]
results = {}
for b in bits_list:
y_adc, Delta = quantize_uniform(x_adc, b, Vmin, Vmax, mid_tread=True)
snr, e = snr_db(x_adc, y_adc)
results[b] = dict(y_adc=y_adc, Delta=Delta, snr_db=snr, err=e)
print("Summary (ADC domain):")Summary (ADC domain):
8-bit -> LSB Δ = 7.812 mV, Measured SNR ≈ 24.1 dB
10-bit -> LSB Δ = 1.953 mV, Measured SNR ≈ 36.0 dB
12-bit -> LSB Δ = 0.488 mV, Measured SNR ≈ 48.1 dB
Input-referred (12-bit): Δ_in = 2.441 µV, σ_q ≈ 0.705 µV RMS
# Plot 1: original vs quantized (10-bit)
b_plot = 10
idx = (t >= 1.5) & (t <= 2.7)
plt.figure()
plt.title(f"ECG (ADC input) vs. {b_plot}-bit quantized")
plt.plot(t[idx], x_adc[idx], label="Original (ADC input)")
plt.plot(t[idx], results[b_plot]["y_adc"][idx], label=f"{b_plot}-bit")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.legend()
plt.grid(True)
plt.show()To explain the analog-to-digital conversion process, we will assume that the input signal is a cosine wave with frequency \(F\), angular frequency \(\Omega\) and amplitude \(a\).
\[x\left(t\right) = a \cos\left(\Omega t + \phi\right) = a \cos\left(2\pi F t + \phi\right)\]
Obtaining
\[x\left[n\right] = a \cos\left(\omega n + \phi\right) = a \cos\left(2\pi f n + \phi\right)\]
What?
Mathematically, the sampling process is:
\[x[n] = x(nT_s), \quad -\infty < n < \infty\]
Replacing in previous equations, we have the expression:
\[x[n] = x(nT_s) = a \cos\left( 2\pi F n T_s + \phi \right) = a \cos\left( 2\pi n \frac{F}{F_s} + \phi \right) \]
Where:
\[\omega = \Omega T_s, \quad f = \frac{F}{F_s}\]
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import chirp
# Generate a synthetic ECG-like signal (chirp function as approximation)
fs_original = 10000 # High sampling rate (Hz) - "continuous" signal
t = np.linspace(0, 1, fs_original, endpoint=False) # 1-second signal
signal = np.sin(2 * np.pi * 1.7 * (t**2)) # Simulated chirp (similar to ECG waves)
# Downsample (Sampling Process)
fs_sampled = 200 # Sampling frequency in Hz (e.g., ECG sampled at 200 Hz)
t_sampled = np.arange(0, 1, 1/fs_sampled)
signal_sampled = np.sin(2 * np.pi * 1.7 * (t_sampled**2))
# Quantization (8-bit and 4-bit)
def quantize(signal, bits):
levels = 2**bits
min_val, max_val = signal.min(), signal.max()
step = (max_val - min_val) / levels
quantized_signal = np.round((signal - min_val) / step) * step + min_val
return quantized_signal
signal_quantized_8bit = quantize(signal_sampled, 8)
signal_quantized_4bit = quantize(signal_sampled, 4)
# Plot Results
plt.figure(figsize=(12, 6))
# Original vs Sampled Signal
plt.subplot(2, 1, 1)
plt.plot(t, signal, 'k', alpha=0.3, label='Original Signal (High Resolution)')
plt.plot(t_sampled, signal_sampled, 'ro-', label=f'Sampled Signal ({fs_sampled} Hz)')
plt.xlabel("Time (s)")
plt.ylabel("Amplitude")
plt.legend()
plt.title("Sampling Process")
# Quantized Signals
plt.subplot(2, 1, 2)
plt.plot(t_sampled, signal_sampled, 'bo-', alpha=0.5, label="Original Sampled")
plt.plot(t_sampled, signal_quantized_8bit, 'go-', label="Quantized 8-bit")
plt.plot(t_sampled, signal_quantized_4bit, 'ro-', label="Quantized 4-bit")
plt.xlabel("Time (s)")
plt.ylabel("Amplitude")
plt.legend()
plt.title("Quantization Effect")
plt.tight_layout()
plt.show()